use std::sync::Arc;
use async_trait::async_trait;
use bincode::{Decode as OtherDecode, Encode as OtherEncode};
use bytes::BytesMut;
use log::info;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::json;
use rpc::error::RpcError;
use rpc::{create_default_rpc_codecs, decode, decode_rpc_header, encode, encode_rpc_data, RpcContext, RpcResult};
use rpc::codec::binary_codec::RpcBinCodeCodec;
use rpc::codec::json_codec::RpcJsonCodec;
use rpc::codec::RpcCodec;
use rpc::dto::{RpcData, RpcHeader};

trait Coder {
    fn encode(&self, bytes_mut: &mut BytesMut);
    fn decode(&self, bytes_mut: &mut BytesMut);
}

#[async_trait]
trait RpcChannelTest<T> {
    async fn write(&mut self, data: Vec<u8>) -> Result<(), RpcError>;

    async fn read(&mut self)-> Result<T, RpcError>;
}

struct RpcConfigTest<T: RpcCodec<T>> {
    channel: Arc<Box<dyn RpcChannelTest<T>>>,
}

struct RpcChannelDemo;

// impl <T>RpcChannelTest<T> for RpcChannelDemo {
//     async fn write(&mut self, data: Box<dyn Encode>) -> Result<(), RpcError> {
//         todo!()
//     }
//
//     async fn read(&mut self) -> Result<T, RpcError> {
//         todo!()
//     }
// }

#[test]
fn test() {
    send("adsada".to_string().as_bytes());
    // send(32);

    let d = 32;
    let json = serde_json::to_string(&d).unwrap();
    info!("json: {}", json);
    let value = json!(json);
    let dd: i32 = serde_json::from_str(&json).unwrap();
}

#[test]
fn codec_json_test()-> RpcResult<()> {
    let data = (12, "hello".to_string());
    let context = RpcContext::test_context_with_codec_type("json");
    let bytes = encode(&context, &data)?;
    let d1 = decode::<(i32, String)>(&context, bytes)?;
    assert_eq!(d1, data);
    Ok(())
}

#[test]
fn codec_json_code_test2()-> RpcResult<()> {
    let data = (12, "hello".to_string());
    let context = RpcContext::test_context_with_codec_type("json");
    let rpc_data = RpcData::build(RpcHeader::new("bin-code-test", "demo".to_string(), 0), data);

    let encode_bytes = encode_rpc_data(&context, &rpc_data)?;
    let (header, dd) = decode_rpc_header(&context, &encode_bytes[4..].to_vec())?;
    assert_eq!(header.path, "bin-code-test".to_string());
    let d1 = decode::<(i32, String)>(&context, dd)?;
    assert_eq!(d1, (12, "hello".to_string()));
    Ok(())
}

#[test]
fn codec_bin_code_test()-> RpcResult<()> {
    let data = (12, "hello".to_string());
    let context = RpcContext::test_context_with_codec_type("bin-code");
    let bytes = encode(&context, &data)?;
    let d1 = decode::<(i32, String)>(&context, bytes)?;
    assert_eq!(d1, data);
    Ok(())
}

#[test]
fn codec_bin_code_test2()-> RpcResult<()> {
    let data = (12, "hello".to_string());
    let context = RpcContext::test_context_with_codec_type("bin-code");
    let rpc_data = RpcData::build(RpcHeader::new("bin-code-test", "demo".to_string(), 0), data);

    let encode_bytes = encode_rpc_data(&context, &rpc_data)?;
    let (header, dd) = decode_rpc_header(&context, &encode_bytes[4..].to_vec())?;

    assert_eq!(header.path, "bin-code-test".to_string());
    assert_eq!(header.version, 1);
    assert_eq!(header.status, 0);
    assert_eq!(header.local_seq().unwrap(), "demo".to_string());
    let d1 = decode::<(i32, String)>(&context, dd)?;
    assert_eq!(d1, (12, "hello".to_string()));
    Ok(())
}


pub trait Iterator<T> {
    fn next(&mut self) -> Option<T>;
}

fn send(data: &[u8]) {
    info!("111111111111111111111");
}



#[test]
fn codec_test() -> RpcResult<()> {
    let mut json_codec = JsonCodec::new();
    json_codec.encode(&32i32)?;
    info!("json: {:?}", json_codec);
    let value : RpcResult<i32> = json_codec.decode();
    info!("value: {:?}", value);
    Ok(())
}

#[test]
fn codec_test2()->RpcResult<()> {
    let mut json_codec = JsonCodec::new();

    let demo = DemoDto {
        u1: 0,
        f1: 0.0,
        male: DemoEnum::Sex,
        is_succes: false,
    };
    json_codec.encode(&demo)?;
    info!("json: {:?}", json_codec);
    let value : RpcResult<DemoDto> = json_codec.decode();
    info!("value: {:?}", value);
    assert_eq!(demo, value.unwrap());
    Ok(())
}

#[test]
fn codec_test3()->RpcResult<()> {
    let mut bind_codec = BincodeCodec::new();

    let demo = DemoDto {
        u1: 20,
        f1: 0.54,
        male: DemoEnum::Female,
        is_succes: true,
    };
    bind_codec.encode(&demo)?;
    info!("json: {:?}", bind_codec);
    let value : RpcResult<DemoDto> = bind_codec.decode();
    info!("value: {:?}", value);
    assert_eq!(demo, value.unwrap());
    Ok(())
}

pub trait ObjectCodec<T> {

    fn encode(&mut self, data: &T) -> RpcResult<()>;

    fn decode(&mut self) -> RpcResult<T>;
}

pub trait Encode<T: From<Self>>: Sized {

    fn encode(self, codec: &mut dyn ObjectCodec<T>) -> RpcResult<()> {
        let t = self.into();
        codec.encode(&t)
    }
}

pub trait Decode<T = Self> {
    fn decode(&self, codec: &mut dyn ObjectCodec<T>) -> RpcResult<T> {
        codec.decode()
    }
}

#[derive(Debug)]
struct JsonCodec {
    json: String,
}

impl JsonCodec {
    pub fn new() -> JsonCodec {
        JsonCodec { json: "".to_string() }
    }
}

impl <T: Serialize + DeserializeOwned> ObjectCodec<T> for JsonCodec {
    fn encode(&mut self, data: &T) -> RpcResult<()> {
        let json = serde_json::to_string::<T>(&data)?;
        self.json.push_str(&json);
        Ok(())
    }

    fn decode(&mut self) -> RpcResult<T> {
        let str = &self.json;
        let r = serde_json::from_str(&self.json);
        match r {
            Ok(r) => { Ok(r) }
            Err(e) => { Err(RpcError::EncodeError(e)) }
        }
    }
}

#[derive(Debug)]
struct BincodeCodec {
    buff: Vec<u8>,
}

impl BincodeCodec {
  pub fn new() -> BincodeCodec {
      BincodeCodec { buff: vec![] }
  }
}

impl <T: bincode::Encode + bincode::Decode<()>> ObjectCodec<T> for BincodeCodec {
    fn encode(&mut self, data: &T) -> RpcResult<()> {
        let bytes = bincode::encode_to_vec(data, bincode::config::standard())?;
        self.buff.extend(bytes);
        Ok(())
    }

    fn decode(&mut self) -> RpcResult<T> {
        let r :(T, usize) = bincode::decode_from_slice(self.buff.as_slice(), bincode::config::standard())?;
        Ok(r.0)
    }
}

impl Encode<i32> for i32 {}

impl Decode for i32 {}

impl Encode<DemoDto> for DemoDto {}
impl Decode for DemoDto {}

#[derive(Serialize, Deserialize, OtherEncode, OtherDecode, PartialEq, Debug)]
struct DemoDto {
    u1: u8,
    f1: f32,
    male: DemoEnum,
    is_succes: bool,
}

#[derive(Serialize, Deserialize, OtherEncode, OtherDecode, PartialEq, Debug)]
enum DemoEnum {
    Sex,
    Female,
}

#[test]
fn codec_gather_test() {
    let gather = create_default_rpc_codecs();
    let tt = gather.has_codec_type("json");
    assert!(tt);
}

#[test]
fn codec_factory_test() {
    let codec_factory = Box::new(DefaultCodecFactory::new());
    let mut context = Context::new(codec_factory);
}

struct Context {
    codec: Box<dyn CodecFactory>,
}

impl Context {
    pub fn new(codec: Box<dyn CodecFactory>) -> Context {
        Context { codec }
    }
}

trait CodecFactory {
    fn encode(&self, codec_type: &'static str, data: &dyn Codec) -> RpcResult<Vec<u8>>;

    fn decode(&mut self, codec_type: &'static str, data: Vec<u8>) -> RpcResult<Box<dyn Codec>>;
}

trait Codec {
    fn encode(&self, codec_type: &'static str)->Vec<u8>;
    fn decode(data: Vec<u8>, codec_type: &'static str)-> Self where Self: Sized;
}

struct DefaultCodecFactory {
    json_codec: RpcJsonCodec,
    bin_codec: RpcBinCodeCodec,
}

impl DefaultCodecFactory {
    pub fn new() -> DefaultCodecFactory {
        DefaultCodecFactory {
            json_codec: RpcJsonCodec,
            bin_codec: RpcBinCodeCodec,
        }
    }
}

impl CodecFactory for DefaultCodecFactory {
    fn encode(&self, codec_type: &'static str, data: &dyn Codec) -> RpcResult<Vec<u8>> {
        if codec_type == "json" {
            Ok(data.encode(codec_type))
        } else if codec_type == "bin-codec" {
            Ok(data.encode(codec_type))
        } else {
            panic!("unknown codec type {}", codec_type);
        }
    }

    fn decode(&mut self, codec_type: &'static str, data: Vec<u8>) -> RpcResult<Box<dyn Codec>> {
        // if codec_type == "json" {
        //     let c = Codec::decode(data, codec_type)?;
        //     Ok(Box::new(Codec::decode(data, codec_type)))
        // } else if codec_type == "bin-codec" {
        //     Ok(Box::new(Codec::decode(data, codec_type)))
        // }
        panic!("unknown codec type {}", codec_type);
    }
}